﻿//-----------------------------------------------------------------------
// <copyright>
//     Copyright (c) Artur Mustafin. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Expressions.Compiler
{
    using System.Collections.Generic;
    using System.Text;

    using Interfaces;
    using Internal;


#if TRACE
    using System.Diagnostics;
#endif

    /// <summary>
    /// 
    /// </summary>
    public sealed class Parser<T>
    {
        private readonly Stack<ExpressionTree<T>> _tree;
        private readonly Stack<Position<T>> _stack;
        private readonly Reader<T> _reader;

        private Position<T> _current;
        private int _depth;

        /// <summary>
        /// Initializes a new instance of the <see cref="Parser&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="input">The input.</param>
        private Parser(IEnumerable<T> input)
        {
            _tree = new Stack<ExpressionTree<T>>();
            _stack = new Stack<Position<T>>();
            _reader = new Reader<T>(input.GetEnumerator());
            _current = new Position<T>(0, _reader);
        }

        /// <summary>
        /// Gets the input.
        /// </summary>
        /// <value>The parse input.</value>
        internal static string GetText(Position<T> start, Position<T> end)
        {
            StringBuilder sb = new StringBuilder();
            Position<T> current = start;
            while (current != end && current.CanRead)
            {
                sb.Append(current.Value);
                current = current.Read();
            }
            return sb.ToString();
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return GetText(_current, null);
        }

#if TRACE
       
        /// <summary>
        /// Performs an implicit conversion from <see cref="Expressions.Compiler.Parser<T>"/> to <see cref="System.String"/>.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator string(Parser<T> input)
        {
            if (input != null)
            {
                return GetText(input._current, null);
            }
            return null;
        }
#endif
        /// <summary>
        /// Performs an implicit conversion from <see cref="Expressions.Compiler.Parser<T>"/> to <see cref="System.String"/>.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator T[](Parser<T> input)
        {
            if (input != null)
            {
                return null;
            }
            return null;
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="Expressions.Compiler.Parser<T>"/>.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Parser<T>(T[] input)
        {
            if (input != null)
            {
                return new Parser<T>(input);
            }
            return null;
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="Expressions.Compiler.Parser<T>"/>.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Parser<T>(T input)
        {
            if (input != null)
            {
                return new Parser<T>(new T[] { input });
            }
            return null;
        }

        /// <summary>
        /// Gets the current.
        /// </summary>
        /// <value>The current.</value>
        internal Position<T> Current
        {
            get
            {
                return _current;
            }
        }

        /// <summary>
        /// Accepts the specified token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="current">The current.</param>
        internal void Accept()
        {
            _depth = _depth - 1;
            if (_stack.Count > 0)
            {
                _stack.Pop();
            }
        }

        internal void Push()
        {
            _depth = _depth + 1;
            _stack.Push(_current);
        }

        internal void Cancel()
        {
            if (_tree.Count > 0)
            {
                while (_tree.Count > 0 && _tree.Peek().Depth >= _depth)
                {
                    _tree.Pop();
                }
            }
            if (_stack.Count > 0)
            {
                _current = _stack.Peek();
            }
        }

#if !RECURSION
        internal bool Read(Terminal<T> terminal)
        {
            bool result = terminal.Read(_current);
            if (_stack.Count > 0)
            {
                _current = _stack.Peek();
            }
            return result;
        }
#endif

        internal void Pop()
        {
            _depth = _depth - 1;
            if (_stack.Count > 0)
            {
                _current = _stack.Pop();
            }
        }

        public void Reset()
        {
            if (_current != null)
            {
                _tree.Clear();
                _stack.Clear();
                _reader.Reset();
                _current = null;
                _depth = 0;
            }
        }

        internal void Read(Position<T> current)
        {
            while (_current != null && _current != current && _current.CanRead)
            {
                _current = _current.Read();
            }
        }

        /// <summary>
        /// Accepts the specified input.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <param name="context">The context.</param>
        internal void Parse(IExpressionContext<T> context)
        {
            Stack<ExpressionTree<T>> children = null;
            if (_tree.Count > 0 && _tree.Peek().Depth == _depth + 1)
            {
                children = new Stack<ExpressionTree<T>>();
                Pop(children);
            }
            Push(context, children);
        }

        /// <summary>
        /// Accepts the specified input.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <param name="context">The context.</param>
        /// <param name="tokens">The tokens.</param>
        internal void Parse(IExpressionContext<T> context, IEnumerable<IExpressionTree<T>> tokens)
        {
            Stack<ExpressionTree<T>> children = null;
            if (tokens != null)
            {
                children = new Stack<ExpressionTree<T>>();
                foreach (IExpressionTree<T> item in tokens)
                {
                    if (_tree.Count > 0)
                    {
                        ExpressionTree<T> root = _tree.Peek();
                        if (root.Depth == _depth)
                        {
                            if (root.Context.Token.Equals(item.Context.Token))
                            {
                                Pop(children);
                            }
                        }
                    }
                }
            }
            Push(context, children);
        }

        private void Push(IExpressionContext<T> context, IEnumerable<IExpressionTree<T>> children)
        {
            _tree.Push(CreateExpressionTree(context, children));
#if TRACE
            WriteLine(context.Name, _depth, context.ToString());
#endif
        }

        private void Pop(Stack<ExpressionTree<T>> children)
        {
            children.Push(_tree.Pop());
        }

        public IExpressionTree<T> Tree
        {
            get
            {
                if (_tree.Count == 1)
                {
                    return _tree.Peek();
                }
                return null;
            }
        }

#if TRACE
        private static List<string> workItems = new List<string>();

        private static int counter;

        private static void WriteLine(string name, int offset, string info)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < offset; i++)
            {
                sb.Append(' ');
            }
            sb.AppendFormat("[ {0} ] {1}", name, info);
            workItems.Insert(0, sb.ToString());
            WriteTraceLog();
        }

        public static void ClearTraceLog()
        {
            workItems.Clear();
        }

        public static void WriteTraceLog()
        {
            StringBuilder sb = new StringBuilder();
            while (workItems.Count > 0)
            {
                sb.AppendFormat("{0}:", ++counter);
                sb.AppendLine(workItems[workItems.Count - 1]);
                workItems.RemoveAt(workItems.Count - 1);
            }
            Trace.Write(sb.ToString());
        }

#endif

        internal string Text
        {
            get
            {
                return GetText();
            }
        }

        internal string GetText()
        {
            StringBuilder sb = new StringBuilder();
            GetTerminalTree(_tree, sb);
            return sb.ToString();
        }

        private void GetTerminalTree(IEnumerable<IExpressionTree<T>> tree, StringBuilder sb)
        {
            foreach (ExpressionTree<T> node in tree)
            {
                Terminal<T> terminal = node.Context.Token as Terminal<T>;
                if (terminal != null)
                {
                    if (object.ReferenceEquals(terminal.Value, Terminal<T>.Default))
                    {
                        break;
                    }
                    sb.Append(node.Context);
                }
                else
                {
                    if (node.Children != null)
                    {
                        GetTerminalTree(node.Children, sb);
                    }
                }
            }
        }


#if TRACE
        internal ExpressionTree<T> CreateExpressionTree(IExpressionContext<T> context, IEnumerable<IExpressionTree<T>> children)
        {
            return new ExpressionTree<T>(_depth, context, children);
        }

        internal ExpressionTree<T> CreateExpressionTree(Entity<T> entity, string name, Position<T> start, Position<T> end)
        {
            return new ExpressionTree<T>(_depth, CreateExpressionContext(entity, name, start, end));
        }

        private IExpressionContext<T> CreateExpressionContext(Entity<T> entity, string name, Position<T> start, Position<T> end)
        {
            return new ExpressionContext<T>(entity, name, start, end);
        }

        internal void Parse(Entity<T> entity, string name, Position<T> start, Position<T> end)
        {
            Parse(CreateExpressionContext(entity, name, start, end));
        }

        internal void Parse(IEnumerable<IExpressionTree<T>> tokens, string name, Position<T> start, Position<T> end)
        {
            Parse(CreateExpressionContext(null, name, start, end), tokens);
        }
#else
        internal ExpressionTree<T> CreateExpressionTree(IExpressionContext<T> context, IEnumerable<IExpressionTree<T>> children)
        {
            return new ExpressionTree<T>(_depth, context, children);
        }

        internal ExpressionTree<T> CreateExpressionTree(Entity<T> entity, Position<T> start, Position<T> end)
        {
            return new ExpressionTree<T>(_depth, CreateExpressionContext(entity, start, end));
        }

        private IExpressionContext<T> CreateExpressionContext(Entity<T> entity, Position<T> start, Position<T> end)
        {
            return new ExpressionContext<T>(entity, start, end);
        }

        internal void Parse(Entity<T> entity, Position<T> start, Position<T> end)
        {
            Parse(CreateExpressionContext(entity, start, end));
        }

        internal void Parse(IEnumerable<IExpressionTree<T>> tokens, Position<T> start, Position<T> end)
        {
            Parse(CreateExpressionContext(null, start, end), tokens);
        }
#endif
    }

    /// <summary>
    /// 
    /// </summary>
    public sealed class Parser
    {
        private readonly Stack<ExpressionTree> _tree;
        private readonly Stack<Position> _stack;
        private readonly Reader _reader;

        private Position _current;
        private int _depth;

        /// <summary>
        /// Initializes a new instance of the <see cref="Parser"/> class.
        /// </summary>
        private Parser(string input)
        {
            _tree = new Stack<ExpressionTree>();
            _stack = new Stack<Position>();
            _reader = new Reader(input);
            _current = new Position(0, _reader);
        }

        /// <summary>
        /// Gets the input.
        /// </summary>
        /// <value>The parse input.</value>
        internal static string GetText(Position start, Position end)
        {
            StringBuilder sb = new StringBuilder();
            Position current = start;
            while (current != end && current.CanRead)
            {
                sb.Append(current.Char);
                current = current.Read();
            }
            return sb.ToString();
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return GetText(_current, null);
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="Expressions.Compiler.Parser"/> to <see cref="System.String"/>.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator string(Parser input)
        {
            if (input != null)
            {
                return GetText(input._current, null);
            }
            return null;
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="Expressions.Compiler.Parser"/>.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Parser(string input)
        {
            if (input != null)
            {
                return new Parser(input);
            }
            return null;
        }

        /// <summary>
        /// Gets the current.
        /// </summary>
        /// <value>The current.</value>
        internal Position Current
        {
            get
            {
                return _current;
            }
        }

        /// <summary>
        /// Accepts the specified token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="current">The current.</param>
        internal void Accept()
        {
            _depth = _depth - 1;
            if (_stack.Count > 0)
            {
                _stack.Pop();
            }
        }

        internal void Push()
        {
            _depth = _depth + 1;
            _stack.Push(_current);
        }

        internal void Cancel()
        {
            if (_tree.Count > 0)
            {
                while (_tree.Count > 0 && _tree.Peek().Depth >= _depth)
                {
                    _tree.Pop();
                }
            }
            if (_stack.Count > 0)
            {
                _current = _stack.Peek();
            }
        }

#if !RECURSION
        internal bool Read(Terminal terminal)
        {
            bool result = terminal.Read(_current);
            if (_stack.Count > 0)
            {
                _current = _stack.Peek();
            }
            return result;
        }
#endif

        internal void Pop()
        {
            _depth = _depth - 1;
            if (_stack.Count > 0)
            {
                _current = _stack.Pop();
            }
        }

        public void Close()
        {
            if (_current != null)
            {
                _reader.Close();
                _current = null;
            }
        }

        internal void Read(Position current)
        {
            while (_current != null && _current != current && _current.CanRead)
            {
                _current = _current.Read();
            }
        }

        /// <summary>
        /// Accepts the specified input.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <param name="context">The context.</param>
        internal void Parse(IExpressionContext context)
        {
            Stack<ExpressionTree> children = null;
            if (_tree.Count > 0 && _tree.Peek().Depth == _depth + 1)
            {
                children = new Stack<ExpressionTree>();
                Pop(children);
            }
            Push(context, children);
        }

        /// <summary>
        /// Accepts the specified input.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <param name="context">The context.</param>
        /// <param name="tokens">The tokens.</param>
        internal void Parse(IExpressionContext context, IEnumerable<IExpressionTree> tokens)
        {
            Stack<ExpressionTree> children = null;
            if (tokens != null)
            {
                children = new Stack<ExpressionTree>();
                foreach (IExpressionTree item in tokens)
                {
                    if (_tree.Count > 0)
                    {
                        ExpressionTree root = _tree.Peek();
                        if (root.Depth == _depth)
                        {
                            if (root.Context.Token.Equals(item.Context.Token))
                            {
                                Pop(children);
                            }
                        }
                    }
                }
            }
            Push(context, children);
        }

        private void Push(IExpressionContext context, IEnumerable<IExpressionTree> children)
        {
            _tree.Push(CreateExpressionTree(context, children));
#if TRACE
            WriteLine(context.Name, _depth, context.ToString());
#endif
        }

        private void Pop(Stack<ExpressionTree> children)
        {
            children.Push(_tree.Pop());
        }

        public IExpressionTree Tree
        {
            get
            {
                if (_tree.Count == 1)
                {
                    return _tree.Peek();
                }
                return null;
            }
        }

#if TRACE
        private static List<string> workItems = new List<string>();

        private static int counter;

        private static void WriteLine(string name, int offset, string info)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < offset; i++)
            {
                sb.Append(' ');
            }
            sb.AppendFormat("[ {0} ] {1}", name, info);
            workItems.Insert(0, sb.ToString());
            WriteTraceLog();
        }

        public static void ClearTraceLog()
        {
            workItems.Clear();
        }

        public static void WriteTraceLog()
        {
            StringBuilder sb = new StringBuilder();
            while (workItems.Count > 0)
            {
                sb.AppendFormat("{0}:", ++counter);
                sb.AppendLine(workItems[workItems.Count - 1]);
                workItems.RemoveAt(workItems.Count - 1);
            }
            Trace.Write(sb.ToString());
        }

        internal string Text
        {
            get
            {
                return GetText();
            }
        }

        private string GetText()
        {
            StringBuilder sb = new StringBuilder();
            GetTerminalTree(_tree, sb);
            return sb.ToString();
        }
#endif


        private void GetTerminalTree(IEnumerable<IExpressionTree> tree, StringBuilder sb)
        {
            foreach (ExpressionTree node in tree)
            {
                Terminal terminal = node.Context.Token as Terminal;
                if (terminal != null)
                {
                    if (terminal.Value != Terminal.Null.ToString())
                    {
                        sb.Append(node.Context);
                    }
                }
                else
                {
                    if (node.Children != null)
                    {
                        GetTerminalTree(node.Children, sb);
                    }
                }
            }
        }

        internal ExpressionTree CreateExpressionTree(IExpressionContext context, IEnumerable<IExpressionTree> children)
        {
            return new ExpressionTree(_depth, context, children);
        }

#if TRACE
        internal ExpressionTree CreateExpressionTree(Entity entity, string name, Position start, Position end)
        {
            return new ExpressionTree(_depth, CreateExpressionContext(entity, name, start, end));
        }

        private IExpressionContext CreateExpressionContext(Entity entity, string name, Position start, Position end)
        {
            return new ExpressionContext(entity, name, start, end);
        }

        internal void Parse(Entity entity, string name, Position start, Position end)
        {
            Parse(CreateExpressionContext(entity, name, start, end));
        }

        internal void Parse(IEnumerable<IExpressionTree> tokens, string name, Position start, Position end)
        {
            Parse(CreateExpressionContext(null, name, start, end), tokens);
        }
#else
        internal ExpressionTree CreateExpressionTree(Entity entity, Position start, Position end)
        {
            return new ExpressionTree(_depth, CreateExpressionContext(entity, start, end));
        }

        private IExpressionContext CreateExpressionContext(Entity entity, Position start, Position end)
        {
            return new ExpressionContext(entity, start, end);
        }

        internal void Parse(Entity entity, Position start, Position end)
        {
            Parse(CreateExpressionContext(entity, start, end));
        }

        internal void Parse(IEnumerable<IExpressionTree> tokens, Position start, Position end)
        {
            Parse(CreateExpressionContext(null, start, end), tokens);
        }
#endif
    }
}
